// ① 一行开关：true=连模拟器，false=连真机
const USE_SIMU = true;
const SIMU_URL = 'ws://localhost:8765';

// 请根据实际设备的 Service 和 Characteristic UUID 修改！！
const CPR_SERVICE_UUID = '0000ffe0-0000-1000-8000-00805f9b34fb';
const CPR_CHARACTERISTIC_UUID = '0000ffe1-0000-1000-8000-00805f9b34fb';

// const modelCode = new Uint8Array([0x00, 0x00, 0x00]); // 模型编号，3字节
const modelCode = new Uint8Array([0x19, 0x08, 0x16]); // 模型名称，3字节
let gattServer = null;
let cprCharacteristic = null;

// ===== 波形图类定义 =====
class WaveformChart {
  constructor(canvasId, maxValue, color = '#007bff', label = '', maxDataPoints = 150) {
    this.canvas = document.getElementById(canvasId);
    this.ctx = this.canvas.getContext('2d');
    this.maxValue = maxValue;
    this.color = color;
    this.label = label;
    this.data = [];
    this.colorData = []; // 存储每个数据点的颜色信息
    this.maxDataPoints = maxDataPoints; // 可配置的最大数据点数
    this.gridColor = '#e0e0e0';
    this.textColor = '#666';
    this.isCompressionChart = canvasId === 'compressionWaveform'; // 判断是否为按压波形图
    this.isVentilationChart = canvasId === 'ventilationWaveform'; // 判断是否为通气波形图
    this.setupCanvas();
  }
  
  setupCanvas() {
    // 设置高DPI显示
    const rect = this.canvas.getBoundingClientRect();
    const dpr = window.devicePixelRatio || 1;
    this.canvas.width = rect.width * dpr;
    this.canvas.height = rect.height * dpr;
    this.ctx.scale(dpr, dpr);
    this.canvas.style.width = rect.width + 'px';
    this.canvas.style.height = rect.height + 'px';
    
    this.width = rect.width;
    this.height = rect.height;
    this.padding = { top: 20, right: 20, bottom: 40, left: 50 };
    this.chartWidth = this.width - this.padding.left - this.padding.right;
    this.chartHeight = this.height - this.padding.top - this.padding.bottom;
  }
  
  addData(value, color = null) {
    this.data.push(value);
    
    // 为按压波形图添加颜色信息
    if (this.isCompressionChart && value > 0) {
      let dataColor;
      if (value >= 50 && value <= 60) {
        dataColor = '#27ae60'; // 绿色 - 正确
      } else if (value < 50) {
        dataColor = '#f39c12'; // 橙色 - 过浅
      } else {
        dataColor = '#e74c3c'; // 红色 - 过深
      }
      this.colorData.push(dataColor);
    } else if (this.isVentilationChart && value > 0) {
      // 为通气波形图添加颜色信息
      let dataColor;
      if (value >= 500 && value <= 600) {
        dataColor = '#27ae60'; // 绿色 - 正确 (500-600ml)
      } else if (value < 500) {
        dataColor = '#f39c12'; // 橙色 - 过小 (<500ml)
      } else {
        dataColor = '#e74c3c'; // 红色 - 过大 (>600ml)
      }
      this.colorData.push(dataColor);
    } else {
      // 默认颜色
      this.colorData.push(color || this.color);
    }
    
    // 移除超出范围的数据点
    if (this.data.length > this.maxDataPoints) {
      this.data.shift();
      this.colorData.shift();
    }
    this.draw();
  }
  
  draw() {
    this.ctx.clearRect(0, 0, this.width, this.height);
    
    // 绘制网格
    this.drawGrid();
    
    // 绘制坐标轴
    this.drawAxes();
    
    // 绘制波形
    this.drawWaveform();
    
    // 绘制当前值
    this.drawCurrentValue();
  }
  
  drawGrid() {
    this.ctx.strokeStyle = this.gridColor;
    this.ctx.lineWidth = 1;
    
    // 水平网格线
    const horizontalLines = 5;
    for (let i = 0; i <= horizontalLines; i++) {
      const y = this.padding.top + (this.chartHeight / horizontalLines) * i;
      this.ctx.beginPath();
      this.ctx.moveTo(this.padding.left, y);
      this.ctx.lineTo(this.padding.left + this.chartWidth, y);
      this.ctx.stroke();
    }
    
    // 垂直网格线
    const verticalLines = 10;
    for (let i = 0; i <= verticalLines; i++) {
      const x = this.padding.left + (this.chartWidth / verticalLines) * i;
      this.ctx.beginPath();
      this.ctx.moveTo(x, this.padding.top);
      this.ctx.lineTo(x, this.padding.top + this.chartHeight);
      this.ctx.stroke();
    }
  }
  
  drawAxes() {
    this.ctx.strokeStyle = '#333';
    this.ctx.lineWidth = 2;
    this.ctx.fillStyle = this.textColor;
    this.ctx.font = '12px Arial';
    
    // Y轴
    this.ctx.beginPath();
    this.ctx.moveTo(this.padding.left, this.padding.top);
    this.ctx.lineTo(this.padding.left, this.padding.top + this.chartHeight);
    this.ctx.stroke();
    
    // X轴
    this.ctx.beginPath();
    this.ctx.moveTo(this.padding.left, this.padding.top + this.chartHeight);
    this.ctx.lineTo(this.padding.left + this.chartWidth, this.padding.top + this.chartHeight);
    this.ctx.stroke();
    
    // Y轴刻度和标签
    const yLabels = 5;
    for (let i = 0; i <= yLabels; i++) {
      const value = (this.maxValue / yLabels) * (yLabels - i);
      const y = this.padding.top + (this.chartHeight / yLabels) * i;
      const label = value.toFixed(0);
      
      this.ctx.textAlign = 'right';
      this.ctx.textBaseline = 'middle';
      this.ctx.fillText(label, this.padding.left - 10, y);
    }
    
    // 单位标签
    this.ctx.textAlign = 'center';
    this.ctx.textBaseline = 'middle';
    this.ctx.save();
    this.ctx.translate(15, this.padding.top + this.chartHeight / 2);
    this.ctx.rotate(-Math.PI / 2);
    this.ctx.fillText(this.label, 0, 0);
    this.ctx.restore();
  }
  
  drawWaveform() {
    if (this.data.length < 1) return;
    
    // 通气波形图使用柱状图显示
    if (this.isVentilationChart) {
      this.drawBarChart();
    } else {
      // 按压波形图使用线条图显示
      this.drawLineChart();
    }
  }
  
  drawLineChart() {
    this.ctx.lineWidth = 3;
    
    // 按压波形图：每段线条使用对应的颜色
    for (let i = 1; i < this.data.length; i++) {
      const x1 = this.padding.left + (this.chartWidth / (this.maxDataPoints - 1)) * (i - 1);
      const y1 = this.padding.top + this.chartHeight - (this.data[i - 1] / this.maxValue) * this.chartHeight;
      const x2 = this.padding.left + (this.chartWidth / (this.maxDataPoints - 1)) * i;
      const y2 = this.padding.top + this.chartHeight - (this.data[i] / this.maxValue) * this.chartHeight;
      
      // 使用当前点的颜色
      this.ctx.strokeStyle = this.colorData[i] || this.color;
      this.ctx.beginPath();
      this.ctx.moveTo(x1, y1);
      this.ctx.lineTo(x2, y2);
      this.ctx.stroke();
    }
    
    // 绘制数据点
    for (let i = 0; i < this.data.length; i++) {
      const x = this.padding.left + (this.chartWidth / (this.maxDataPoints - 1)) * i;
      const y = this.padding.top + this.chartHeight - (this.data[i] / this.maxValue) * this.chartHeight;
      
      this.ctx.fillStyle = this.colorData[i] || this.color;
      this.ctx.beginPath();
      this.ctx.arc(x, y, 3, 0, 2 * Math.PI);
      this.ctx.fill();
    }
  }
  
  drawBarChart() {
    // 计算柱状图的宽度
    const barWidth = this.chartWidth / this.maxDataPoints;
    const barMargin = barWidth * 0.1; // 柱子之间的间距
    const actualBarWidth = barWidth - barMargin;
    
    // 绘制柱状图
    for (let i = 0; i < this.data.length; i++) {
      const x = this.padding.left + barWidth * i + barMargin / 2;
      const barHeight = (this.data[i] / this.maxValue) * this.chartHeight;
      const y = this.padding.top + this.chartHeight - barHeight;
      
      // 使用对应的颜色
      this.ctx.fillStyle = this.colorData[i] || this.color;
      this.ctx.fillRect(x, y, actualBarWidth, barHeight);
      
      // 添加柱子的边框
      this.ctx.strokeStyle = '#fff';
      this.ctx.lineWidth = 1;
      this.ctx.strokeRect(x, y, actualBarWidth, barHeight);
      
      // 在柱子顶部显示数值
      if (this.data[i] > 0) {
        this.ctx.fillStyle = '#333';
        this.ctx.font = '10px Arial';
        this.ctx.textAlign = 'center';
        this.ctx.textBaseline = 'bottom';
        this.ctx.fillText(this.data[i].toString(), x + actualBarWidth / 2, y - 2);
      }
    }
  }
  
  drawCurrentValue() {
    if (this.data.length === 0) return;
    
    const currentValue = this.data[this.data.length - 1];
    const x = this.padding.left + this.chartWidth;
    const y = this.padding.top + this.chartHeight - (currentValue / this.maxValue) * this.chartHeight;
    
    // 绘制当前值点
    this.ctx.fillStyle = this.color;
    this.ctx.beginPath();
    this.ctx.arc(x - 10, y, 4, 0, 2 * Math.PI);
    this.ctx.fill();
    
    // 绘制当前值标签
    this.ctx.fillStyle = '#333';
    this.ctx.font = 'bold 14px Arial';
    this.ctx.textAlign = 'right';
    this.ctx.textBaseline = 'middle';
    this.ctx.fillText(currentValue.toFixed(1), x - 20, y - 15);
  }
  
  clear() {
    this.data = [];
    this.colorData = [];
    this.ctx.clearRect(0, 0, this.width, this.height);
    this.draw();
  }
  
  // 重新调整画布尺寸（用于响应窗口大小变化）
  resize() {
    this.setupCanvas();
    this.draw();
  }
}

// 创建波形图实例
let compressionChart = null;
let ventilationChart = null;

// ===== 训练统计 =====
let totalPress     = 0;   // 累计按压
let totalVent      = 0;   // 累计吹气
let overDeep       = 0;   // 按压过深计数（>60mm）
let overShallow    = 0;   // 按压过浅计数（<50mm）
let correctPress   = 0;   // 按压深度正确计数（50-60mm）
let correctPosition = 0;  // 按压位置正确计数
let correctRecoil  = 0;   // 按压回弹正确计数
let overVent       = 0;   // 吹气过大（>600ml）
let underVent      = 0;   // 吹气过小（<500ml）
let correctVent    = 0;   // 吹气正确（500-600ml）
let maxDepthValue  = 0;   // 最大按压深度记录
let maxVentValue   = 0;   // 最大通气量记录
let currentScore   = 0;   // 当前评分
const TARGET_PRESS = 150;
const TARGET_VENT  = 10;

// UI 元素
const connectBtn = document.getElementById('connectBtn');
const clearBtn = document.getElementById('clearBtn');
const stopBtn = document.getElementById('stopBtn');
const printLog = document.getElementById('printLog');
const copyLog = document.getElementById('copyLog');
const bluetoothStatus = document.getElementById('bluetoothStatus');

// 按压状态显示元素
const compressionStatusImg = document.getElementById('compressionStatusImg');
const compressionStatusText = document.getElementById('compressionStatusText');

// 数据展示元素
const elements = {
  compressionCount: document.getElementById('compressionCount'),
  compressionDepth: document.getElementById('compressionDepth'),
  compressionPosition: document.getElementById('compressionPosition'),
  compressionRate: document.getElementById('compressionRate'),
  maxCompressionDepth: document.getElementById('maxCompressionDepth'),
  isRecoil: document.getElementById('isRecoil'),
  ventilationCount: document.getElementById('ventilationCount'),
  ventilationVolume: document.getElementById('ventilationVolume'),
  isVentilation: document.getElementById('isVentilation'),
  ventilationDuration: document.getElementById('ventilationDuration'),
  maxVentilationVolume: document.getElementById('maxVentilationVolume')
};

async function connectSimu() {
  bluetoothStatus.textContent = '正在连模拟器...';
  const ws = new WebSocket(SIMU_URL);
  ws.binaryType = 'arraybuffer';
  ws.onopen = () => {
    bluetoothStatus.textContent = '✅ 模拟器已连接';
    connectBtn.disabled = true;
  };
  ws.onmessage = (evt) => parseCPRData(evt.data);
  ws.onclose = () => {
    bluetoothStatus.textContent = '模拟器断开';
    connectBtn.disabled = false;
  };
  window.simuWs = ws;   // 给命令按钮用
}

// 更新按压状态显示
function updateCompressionStatus(depth) {
  if (depth === 0) {
    // 默认状态（无按压）
    compressionStatusImg.src = '../img/cylinder.png';
    compressionStatusImg.className = 'status-image released';
    compressionStatusText.textContent = '默认状态';
    compressionStatusText.className = 'status-text';
  } else {
    // 按压过程中，先显示灰色状态
    compressionStatusImg.className = 'status-image pressing';
    compressionStatusText.textContent = '正在按压中...';
    compressionStatusText.className = 'status-text pressing';
    
    // 短暂延迟后显示最终状态
    setTimeout(() => {
      compressionStatusImg.className = 'status-image released';
      
      if (depth >= 50 && depth <= 60) {
        // 按压正确
        compressionStatusImg.src = '../img/green.png';
        compressionStatusText.textContent = '按压正确（' + depth + 'mm）';
        compressionStatusText.className = 'status-text correct';
      } else if (depth < 50) {
        // 按压过浅
        compressionStatusImg.src = '../img/origin.png';
        compressionStatusText.textContent = '按压过浅（' + depth + 'mm）';
        compressionStatusText.className = 'status-text shallow';
      } else {
        // 按压过深
        compressionStatusImg.src = '../img/red.png';
        compressionStatusText.textContent = '按压过深（' + depth + 'mm）';
        compressionStatusText.className = 'status-text deep';
      }
    }, 200); // 200ms后显示最终状态
  }
}

// 红十字会CPR评分系统（满分100分）
// AHA（美国心脏协会）标准CPR评分算法（修正版）
function calculateCPRScore() {
  let totalScore = 0;
  let scoreBreakdown = {
    depthQuality: 0,      // 按压深度质量（25分）
    rateQuality: 0,       // 按压频率质量（20分）
    positionAccuracy: 0,  // 按压位置准确性（15分）
    recoilComplete: 0,    // 按压完全回弹（10分）
    ventQuality: 0,       // 通气质量（20分）
    interruption: 0       // 按压中断时间最小化（10分）
  };
  
  // 训练完成度系数（关键修正）
  const pressCompletionRate = Math.min(totalPress / TARGET_PRESS, 1);
  const ventCompletionRate = Math.min(totalVent / TARGET_VENT, 1);
  const overallCompletionRate = (pressCompletionRate + ventCompletionRate) / 2;
  
  // 1. 按压深度质量（25分）- 必须考虑完成度
  if (totalPress > 0) {
    const depthAccuracyRate = correctPress / totalPress;
    
    // 基础分数基于正确率，但受完成度限制
    let depthScore = Math.round(depthAccuracyRate * 15 * pressCompletionRate);
    
    // 严重过浅惩罚（8次 * 0.5 = 4.0分）
    const shallowPenalty = Math.min(overShallow * 0.5, 5);
    // 过深轻微惩罚（3次 * 0.2 = 0.6分）
    const deepPenalty = Math.min(overDeep * 0.2, 3);
    
    // 完成度奖励（最多10分），只有完成训练才能获得
    const completionBonus = Math.round(pressCompletionRate * 10);
    
    scoreBreakdown.depthQuality = Math.max(0, depthScore + completionBonus - shallowPenalty - deepPenalty);
    totalScore += scoreBreakdown.depthQuality;
  }
  
  // 2. 按压频率质量（20分）- 需要足够数据才能评估
  if (totalPress >= 30) { // 至少30次按压才能评估频率
    const avgRate = 115; // 从模拟器获取的平均频率
    let frequencyScore = 0;
    
    if (avgRate >= 100 && avgRate <= 120) {
      const deviation = Math.abs(avgRate - 110);
      frequencyScore = Math.round(20 - deviation * 0.5);
    } else {
      const deviation = avgRate < 100 ? (100 - avgRate) : (avgRate - 120);
      frequencyScore = Math.max(0, Math.round(20 - deviation * 1.5));
    }
    
    // 频率分数也受完成度影响
    scoreBreakdown.rateQuality = Math.round(frequencyScore * pressCompletionRate);
    totalScore += scoreBreakdown.rateQuality;
  } else {
    // 数据不足，频率分数为0
    scoreBreakdown.rateQuality = 0;
  }
  
  // 3. 按压位置准确性（15分）- 受完成度影响
  if (totalPress > 0) {
    const positionAccuracy = correctPosition / totalPress;
    const baseScore = Math.round(positionAccuracy * 15);
    scoreBreakdown.positionAccuracy = Math.round(baseScore * pressCompletionRate);
    totalScore += scoreBreakdown.positionAccuracy;
  }
  
  // 4. 按压完全回弹（10分）- 受完成度影响
  if (totalPress > 0) {
    const recoilRate = correctRecoil / totalPress;
    const baseScore = Math.round(recoilRate * 10);
    scoreBreakdown.recoilComplete = Math.round(baseScore * pressCompletionRate);
    totalScore += scoreBreakdown.recoilComplete;
  }
  
  // 5. 通气质量（20分）- 受完成度影响
  if (totalVent > 0) {
    const ventAccuracyRate = correctVent / totalVent;
    let ventScore = Math.round(ventAccuracyRate * 15);
    const timeBonus = 5; // 时长奖励
    
    const totalVentScore = Math.min(20, ventScore + timeBonus);
    scoreBreakdown.ventQuality = Math.round(totalVentScore * ventCompletionRate);
    totalScore += scoreBreakdown.ventQuality;
  }
  
  // 6. 按压中断时间最小化（10分）- 直接基于完成度
  scoreBreakdown.interruption = Math.round(overallCompletionRate * 10);
  totalScore += scoreBreakdown.interruption;
  
  return {
    total: Math.min(totalScore, 100),
    breakdown: scoreBreakdown
  };
}

// AHA标准评分等级（更严格的标准）
function getScoreGrade(score) {
  if (score >= 85) return { grade: '优秀', color: '#27ae60' };      // AHA标准：85分以上为优秀
  if (score >= 75) return { grade: '良好', color: '#2980b9' };      // AHA标准：75-84为良好
  if (score >= 65) return { grade: '合格', color: '#f39c12' };      // AHA标准：65-74为合格
  if (score >= 50) return { grade: '需改进', color: '#e67e22' };    // AHA标准：50-64需改进
  return { grade: '不合格', color: '#e74c3c' };                  // AHA标准：<50不合格
}

// 更新评分显示
function updateScore() {
  const scoreResult = calculateCPRScore();
  currentScore = scoreResult.total;
  const scoreElement = document.getElementById('currentScore');
  const gradeElement = document.getElementById('scoreGrade');
  
  if (scoreElement && gradeElement) {
    scoreElement.textContent = currentScore;
    const gradeInfo = getScoreGrade(currentScore);
    gradeElement.textContent = gradeInfo.grade;
    gradeElement.style.color = gradeInfo.color;
  }
}

// 显示评分结果弹窗
function showScoreModal() {
  // 计算最终评分
  const scoreResult = calculateCPRScore();
  currentScore = scoreResult.total;
  const gradeInfo = getScoreGrade(currentScore);
  
  // 更新弹窗内容
  document.getElementById('modalFinalScore').textContent = currentScore;
  const gradeElement = document.getElementById('modalFinalGrade');
  gradeElement.textContent = gradeInfo.grade;
  gradeElement.style.color = gradeInfo.color;
  
  // 更新统计数据
  document.getElementById('modalTotalPress').textContent = `${totalPress}/${TARGET_PRESS}`;
  document.getElementById('modalCorrectPress').textContent = correctPress;
  document.getElementById('modalOverDeep').textContent = overDeep;
  document.getElementById('modalOverShallow').textContent = overShallow;
  
  document.getElementById('modalTotalVent').textContent = `${totalVent}/${TARGET_VENT}`;
  document.getElementById('modalCorrectVent').textContent = correctVent;
  document.getElementById('modalOverVent').textContent = overVent;
  document.getElementById('modalUnderVent').textContent = underVent;
  
  // 更新AHA标准评分细则（含扣分说明）
  // 计算各项扣分说明
  
  // 1. 按压深度质量扣分说明（完全按照算法逻辑）
  const pressCompletionRate = Math.min(totalPress / TARGET_PRESS, 1);
  let depthNote = '';
  
  if (totalPress > 0) {
    // 完全按照评分算法计算
    const depthAccuracyRate = correctPress / totalPress;
    const baseScore = Math.round(depthAccuracyRate * 15 * pressCompletionRate);
    const completionBonus = Math.round(pressCompletionRate * 10);
    const shallowPenalty = Math.min(overShallow * 0.5, 5);
    const deepPenalty = Math.min(overDeep * 0.2, 3);
    const finalScore = Math.max(0, baseScore + completionBonus - shallowPenalty - deepPenalty);
    
    // 调试输出
    console.log('深度评分计算:', {
      totalPress, correctPress, overShallow, overDeep,
      depthAccuracyRate, baseScore, completionBonus, 
      shallowPenalty, deepPenalty, finalScore,
      pressCompletionRate
    });
    
    // 生成详细的扣分说明（修正：准确计算各项损失）
    let parts = [];
    
    // 计算理论满分各部分
    const theoreticalBaseScore = Math.round(15 * pressCompletionRate);  // 如果正确率100%的基础分数
    const theoreticalCompletionBonus = Math.round(pressCompletionRate * 10);  // 完成度奖励
    const theoreticalMaxScore = theoreticalBaseScore + theoreticalCompletionBonus;  // 理论最大分数（无惩罚时）
    
    // 计算实际各项分数
    const actualScoreBeforePenalty = baseScore + completionBonus;  // 惩罚前实际分数
    const actualScoreAfterPenalty = finalScore;  // 惩罚后最终分数
    
    // 详细调试输出
    console.log('扣分详细计算:', {
      theoreticalBaseScore,
      theoreticalCompletionBonus, 
      theoreticalMaxScore,
      actualBaseScore: baseScore,
      actualCompletionBonus: completionBonus,
      actualScoreBeforePenalty,
      shallowPenalty,
      deepPenalty,
      actualScoreAfterPenalty,
      totalDeduction: 25 - actualScoreAfterPenalty
    });
    
    // 添加各项损失说明
    const baseScoreLoss = theoreticalBaseScore - baseScore;
    if (baseScoreLoss > 0) {
      const correctRate = (depthAccuracyRate * 100).toFixed(0);
      parts.push(`正确率${correctRate}%损失${baseScoreLoss}分`);
    }
    
    // 完成度奖励损失（通常为0，因为completionBonus已经按完成度计算）
    const completionBonusLoss = 10 - theoreticalCompletionBonus;
    if (completionBonusLoss > 0) {
      const completionRate = (pressCompletionRate * 100).toFixed(0);
      parts.push(`完成度${completionRate}%损失${completionBonusLoss}分`);
    }
    
    if (shallowPenalty > 0) {
      parts.push(`过浅${overShallow}次扣${shallowPenalty.toFixed(1)}分`);
    }
    
    if (deepPenalty > 0) {
      parts.push(`过深${overDeep}次扣${deepPenalty.toFixed(1)}分`);
    }
    
    // 验证计算是否正确
    const calculatedTotalLoss = baseScoreLoss + completionBonusLoss + shallowPenalty + deepPenalty;
    const actualTotalDeduction = 25 - finalScore;
    
    if (parts.length > 0) {
      parts.push(`总计扣${actualTotalDeduction.toFixed(1)}分`);
      depthNote = `「${parts.join('，')}」`;
      
      // 验证计算准确性
      if (Math.abs(calculatedTotalLoss - actualTotalDeduction) > 0.1) {
        console.warn('扣分计算不匹配!', {
          calculatedTotalLoss,
          actualTotalDeduction,
          difference: actualTotalDeduction - calculatedTotalLoss
        });
      }
    }
  }
  
  // 2. 按压频率质量扣分说明（完全按照算法逻辑）
  let rateNote = '';
  if (totalPress < 30) {
    rateNote = `「数据不足（${totalPress}/30次），无法评估频率」`;
  } else {
    // 完全按照评分算法计算
    const avgRate = 115;
    let baseFrequencyScore = 0;
    
    if (avgRate >= 100 && avgRate <= 120) {
      const deviation = Math.abs(avgRate - 110);
      baseFrequencyScore = Math.round(20 - deviation * 0.5);
    } else {
      const deviation = avgRate < 100 ? (100 - avgRate) : (avgRate - 120);
      baseFrequencyScore = Math.max(0, Math.round(20 - deviation * 1.5));
    }
    
    const finalFrequencyScore = Math.round(baseFrequencyScore * pressCompletionRate);
    
    // 生成准确的扣分说明
    let parts = [];
    if (avgRate < 100) {
      const deduction = 20 - baseFrequencyScore;
      parts.push(`频率过慢${avgRate}次/分，扣${deduction}分`);
    } else if (avgRate > 120) {
      const deduction = 20 - baseFrequencyScore;
      parts.push(`频率过快${avgRate}次/分，扣${deduction}分`);
    } else {
      const deviation = Math.abs(avgRate - 110);
      if (deviation > 0) {
        const actualDeduction = 20 - baseFrequencyScore; // 实际扣分
        parts.push(`偏离最佳110次/分${deviation}次，扣${actualDeduction}分`);
      }
    }
    
    if (pressCompletionRate < 1) {
      const completionDeduction = baseFrequencyScore - finalFrequencyScore;
      parts.push(`完成度${(pressCompletionRate*100).toFixed(0)}%额外折扣${completionDeduction}分`);
    }
    
    if (parts.length > 0) {
      rateNote = `「${parts.join('，')}」`;
    }
  }
  
  // 3. 按压位置准确性扣分说明（按实际计算逻辑）
  let positionNote = '';
  if (totalPress > 0) {
    // 完全按照评分算法计算
    const positionAccuracy = correctPosition / totalPress;
    const baseScore = Math.round(positionAccuracy * 15);
    const finalScore = Math.round(baseScore * pressCompletionRate);
    
    // 调试输出
    console.log('位置评分计算:', {
      totalPress, correctPosition, positionAccuracy,
      baseScore, finalScore, pressCompletionRate
    });
    
    // 生成详细的扣分说明
    let parts = [];
    
    // 计算理论满分各部分
    const theoreticalBaseScore = Math.round(15 * pressCompletionRate); // 如果位置100%正确的分数
    
    // 计算各项损失
    const positionAccuracyLoss = theoreticalBaseScore - finalScore; // 位置准确性损失
    const completionLoss = 15 - theoreticalBaseScore; // 完成度损失
    
    // 添加各项损失说明
    if (positionAccuracyLoss > 0) {
      const correctRate = (positionAccuracy * 100).toFixed(0);
      const positionErrorCount = totalPress - correctPosition;
      parts.push(`位置错误${positionErrorCount}次（正确率${correctRate}%）损失${positionAccuracyLoss}分`);
    }
    
    if (completionLoss > 0) {
      const completionRate = (pressCompletionRate * 100).toFixed(0);
      parts.push(`完成度${completionRate}%损失${completionLoss}分`);
    }
    
    // 验证计算是否正确
    const calculatedTotalLoss = positionAccuracyLoss + completionLoss;
    const actualTotalDeduction = 15 - finalScore;
    
    if (parts.length > 0) {
      parts.push(`总计扣${actualTotalDeduction.toFixed(1)}分`);
      positionNote = `「${parts.join('，')}」`;
      
      // 验证计算准确性
      if (Math.abs(calculatedTotalLoss - actualTotalDeduction) > 0.1) {
        console.warn('位置扣分计算不匹配!', {
          calculatedTotalLoss,
          actualTotalDeduction,
          difference: actualTotalDeduction - calculatedTotalLoss
        });
      }
    } else if (finalScore < 15) {
      // 没有明显错误，但得分不满，可能是完成度问题
      const actualTotalDeduction = 15 - finalScore;
      parts.push(`总计扣${actualTotalDeduction.toFixed(1)}分`);
      positionNote = `「${parts.join('，')}」`;
    }
  }
  
  // 4. 按压完全回弹扣分说明（按实际计算逻辑）
  let recoilNote = '';
  if (totalPress > 0) {
    // 完全按照评分算法计算
    const recoilRate = correctRecoil / totalPress;
    const baseScore = Math.round(recoilRate * 10);
    const finalScore = Math.round(baseScore * pressCompletionRate);
    
    // 调试输出
    console.log('回弹评分计算:', {
      totalPress, correctRecoil, recoilRate,
      baseScore, finalScore, pressCompletionRate
    });
    
    // 生成详细的扣分说明
    let parts = [];
    
    // 计算理论满分各部分
    const theoreticalBaseScore = Math.round(10 * pressCompletionRate); // 如果回弹100%正确的分数
    
    // 计算各项损失
    const recoilAccuracyLoss = theoreticalBaseScore - finalScore; // 回弹准确性损失
    const completionLoss = 10 - theoreticalBaseScore; // 完成度损失
    
    // 添加各项损失说明
    if (recoilAccuracyLoss > 0) {
      const correctRate = (recoilRate * 100).toFixed(0);
      const recoilErrorCount = totalPress - correctRecoil;
      parts.push(`回弹不充分${recoilErrorCount}次（正确率${correctRate}%）损失${recoilAccuracyLoss}分`);
    }
    
    if (completionLoss > 0) {
      const completionRate = (pressCompletionRate * 100).toFixed(0);
      parts.push(`完成度${completionRate}%损失${completionLoss}分`);
    }
    
    // 验证计算是否正确
    const calculatedTotalLoss = recoilAccuracyLoss + completionLoss;
    const actualTotalDeduction = 10 - finalScore;
    
    if (parts.length > 0) {
      parts.push(`总计扣${actualTotalDeduction.toFixed(1)}分`);
      recoilNote = `「${parts.join('，')}」`;
      
      // 验证计算准确性
      if (Math.abs(calculatedTotalLoss - actualTotalDeduction) > 0.1) {
        console.warn('回弹扣分计算不匹配!', {
          calculatedTotalLoss,
          actualTotalDeduction,
          difference: actualTotalDeduction - calculatedTotalLoss
        });
      }
    } else if (finalScore < 10) {
      // 没有明显错误，但得分不满，可能是完成度问题
      const actualTotalDeduction = 10 - finalScore;
      parts.push(`总计扣${actualTotalDeduction.toFixed(1)}分`);
      recoilNote = `「${parts.join('，')}」`;
    }
  }
  
  // 5. 通气质量扣分说明（按实际计算逻辑）
  const ventCompletionRate = Math.min(totalVent / TARGET_VENT, 1);
  let ventNote = '';
  if (totalVent > 0) {
    // 完全按照评分算法计算
    const ventAccuracyRate = correctVent / totalVent;
    let ventScore = Math.round(ventAccuracyRate * 15);
    const timeBonus = 5; // 时长奖励
    const totalVentScore = Math.min(20, ventScore + timeBonus);
    const finalScore = Math.round(totalVentScore * ventCompletionRate);
    
    // 调试输出
    console.log('通气评分计算:', {
      totalVent, correctVent, overVent, underVent,
      ventAccuracyRate, ventScore, timeBonus, 
      totalVentScore, finalScore, ventCompletionRate
    });
    
    // 生成详细的扣分说明（解释评分机制）
    let parts = [];
    
    // 计算理论满分各部分
    const theoreticalVentScore = 15; // 如果正确率100%的通气分数
    const theoreticalTimeBonus = 5;  // 固定时长奖励
    const theoreticalTotalVentScore = Math.min(20, theoreticalVentScore + theoreticalTimeBonus);
    const theoreticalMaxScore = Math.round(theoreticalTotalVentScore * ventCompletionRate); // 理论最大分数
    
    // 计算各项损失
    const ventAccuracyLoss = Math.round((theoreticalVentScore - ventScore) * ventCompletionRate); // 正确率损失
    const completionLoss = theoreticalTotalVentScore - Math.round(theoreticalTotalVentScore * ventCompletionRate); // 完成度损失
    
    // 评分机制详细说明
    console.log('通气评分机制详解:', {
      '评分公式': '正确率 × 15分 + 固定5分时长奖励 = 通气基础分，再乘以完成度系数',
      '实际计算': `${ventAccuracyRate.toFixed(2)} × 15 + 5 = ${totalVentScore}，再 × ${ventCompletionRate.toFixed(2)} = ${finalScore}`,
      '理论满分': theoreticalMaxScore,
      '正确率损失': ventAccuracyLoss,
      '完成度损失': completionLoss
    });
    
    // 添加各项损失说明
    if (ventAccuracyLoss > 0) {
      const correctRate = (ventAccuracyRate * 100).toFixed(0);
      const ventErrorCount = overVent + underVent;
      // 详细说明正确率如何影响得分
      const baseScoreLoss = theoreticalVentScore - ventScore; // 15分中损失的部分
      parts.push(`通气错误${ventErrorCount}次（过大${overVent}次，过小${underVent}次），正确率${correctRate}%，基础分损失${baseScoreLoss.toFixed(1)}分，折算后损失${ventAccuracyLoss.toFixed(1)}分`);
    }
    
    if (completionLoss > 0) {
      const completionRate = (ventCompletionRate * 100).toFixed(0);
      parts.push(`完成度${completionRate}%（${totalVent}/${TARGET_VENT}次）损失${completionLoss.toFixed(1)}分`);
    }
    
    // 如果没有错误但得分不满，说明原因
    if (parts.length === 0 && finalScore < theoreticalMaxScore) {
      const completionRate = (ventCompletionRate * 100).toFixed(0);
      if (ventCompletionRate < 1) {
        parts.push(`完成度${completionRate}%（${totalVent}/${TARGET_VENT}次）损失${completionLoss.toFixed(1)}分`);
      }
    }
    
    // 验证计算是否正确
    const calculatedTotalLoss = ventAccuracyLoss + completionLoss;
    const actualTotalDeduction = 20 - finalScore;
    
    if (parts.length > 0) {
      parts.push(`总计扣${actualTotalDeduction.toFixed(1)}分`);
      ventNote = `「${parts.join('，')}」`;
      
      // 验证计算准确性
      if (Math.abs(calculatedTotalLoss - actualTotalDeduction) > 0.1) {
        console.warn('通气扣分计算不匹配!', {
          calculatedTotalLoss,
          actualTotalDeduction,
          difference: actualTotalDeduction - calculatedTotalLoss
        });
      }
    } else if (finalScore < 20) {
      // 没有明显错误，但得分不满，可能是完成度问题
      const actualTotalDeduction = 20 - finalScore;
      parts.push(`总计扣${actualTotalDeduction.toFixed(1)}分`);
      ventNote = `「${parts.join('，')}」`;
    }
  } else {
    ventNote = `「无通气数据，0分」`;
  }
  
  // 6. 中断时间最小化扣分说明
  const overallCompletionRate = (pressCompletionRate + ventCompletionRate) / 2;
  const interruptNote = overallCompletionRate < 1 ? `「训练未完成${((1-overallCompletionRate)*100).toFixed(0)}%，严重影响连续性」` : '';
  
  // 更新评分细则显示（扣分原因直接接在标题后面）
  document.getElementById('depthQualityTitle').innerHTML = `按压深度质量（5-6cm）<span style="color: #e74c3c; font-size: 14px;">${depthNote}</span>`;
  document.getElementById('rateQualityTitle').innerHTML = `按压频率质量（100-120/分）<span style="color: #e74c3c; font-size: 14px;">${rateNote}</span>`;
  document.getElementById('positionAccuracyTitle').innerHTML = `按压位置准确性<span style="color: #e74c3c; font-size: 14px;">${positionNote}</span>`;
  document.getElementById('recoilCompleteTitle').innerHTML = `按压完全回弹<span style="color: #e74c3c; font-size: 14px;">${recoilNote}</span>`;
  document.getElementById('ventQualityTitle').innerHTML = `通气质量（500-600ml/1s）<span style="color: #e74c3c; font-size: 14px;">${ventNote}</span>`;
  document.getElementById('interruptionTitle').innerHTML = `中断时间最小化（<10s）<span style="color: #e74c3c; font-size: 14px;">${interruptNote}</span>`;
  
  // 更新分数显示
  document.getElementById('depthQualityScore').textContent = scoreResult.breakdown.depthQuality;
  document.getElementById('rateQualityScore').textContent = scoreResult.breakdown.rateQuality;
  document.getElementById('positionAccuracyScore').textContent = scoreResult.breakdown.positionAccuracy;
  document.getElementById('recoilCompleteScore').textContent = scoreResult.breakdown.recoilComplete;
  document.getElementById('ventQualityScore').textContent = scoreResult.breakdown.ventQuality;
  document.getElementById('interruptionScore').textContent = scoreResult.breakdown.interruption;
  
  // 显示弹窗
  document.getElementById('scoreModal').style.display = 'block';
}

// 关闭评分弹窗
function closeScoreModal() {
  document.getElementById('scoreModal').style.display = 'none';
}

// 重新开始训练
function restartTraining() {
  closeScoreModal();
  sendStartTrainingCommand();
}

async function sendStartCommand() {
  const cmd = buildCommand(modelCode, 0x01); // 开始训练指令
  if (USE_SIMU) { 
    if (window.simuWs && window.simuWs.readyState === WebSocket.OPEN) {
      window.simuWs.send(cmd); 
    }
    return; 
  }
  if (!cprCharacteristic) return alert('请先连接设备');
  await cprCharacteristic.writeValue(cmd);
}

async function sendEndCommand() {
  const cmd = buildCommand(modelCode, 0x03); // 结束训练指令
  if (USE_SIMU) { 
    if (window.simuWs && window.simuWs.readyState === WebSocket.OPEN) {
      window.simuWs.send(cmd); 
    }
    return; 
  }
  if (!cprCharacteristic) return alert('请先连接设备');
  await cprCharacteristic.writeValue(cmd);
}

// 主连接函数
async function connectToDevice() {
  if (USE_SIMU) return connectSimu();   // WebSocket
  return connectBle();                  // 你现有 WebBluetooth

  try {
    bluetoothStatus.textContent = '正在请求蓝牙设备...';
    bluetoothStatus.className = 'status-disconnected';

    // 1. 请求蓝牙设备（可过滤，这里接受所有）
    const device = await navigator.bluetooth.requestDevice({
      filters: [{ namePrefix: 'CPR' }],
      optionalServices: [CPR_SERVICE_UUID], // 可选：提前声明要用的服务
    });

    bluetoothStatus.textContent = '正在连接 GATT...';

    // 2. 连接 GATT 服务器
    const server = await device.gatt.connect();
    gattServer = server;
    bluetoothStatus.textContent = '✅ 已连接';
    bluetoothStatus.className = 'status-connected';
    connectBtn.disabled = true;

    // 3. 获取自定义 CPR 服务
    const service = await server.getPrimaryService(CPR_SERVICE_UUID);

    // 4. 获取 CPR 数据特征
    cprCharacteristic = await service.getCharacteristic(CPR_CHARACTERISTIC_UUID);

    // 5. 开始监听数据变化（通知）
    await cprCharacteristic.startNotifications();
    cprCharacteristic.addEventListener('characteristicvaluechanged', event => {
      const value = event.target.value;
      parseCPRData(value);
    });
  } catch (error) {
    bluetoothStatus.textContent = '❌ 连接失败: ' + error.message;
    bluetoothStatus.className = 'status-disconnected';
    connectBtn.disabled = false;
  }
}

// 解析 CPR 数据
function parseCPRData(arrayBuffer) {
  const data = arrayBuffer instanceof DataView ? arrayBuffer : new DataView(arrayBuffer);
  if (data.byteLength < 19) { // 2*9+1=19
    console.warn('收到的数据长度异常:', data.byteLength);
    return;
  }
  let offset = 0;

  const uint8Array = new Uint8Array(data.buffer);
  const dataArray = Array.from(uint8Array);

  // 当前系统时间
  const now = new Date();

  // console.log('数据解析开始', uint8Array, dataArray);

  const header = dataArray[0]; // 数据头 0x5A 
  // 模型编号 3字节 高位在前 低位在后
  const model = dataArray.slice(1, 4);
  const command = dataArray[4];
  const length = dataArray[5]; // 数据长度 0x0E

  if (header !== 0x5A) { // 数据头 0x5A 0xA5
    return;
  }
  
  // 解析数据
  // 按压计数
  const count = (dataArray[6] << 8) | dataArray[7]; // 按压计数
  const depth = dataArray[8] // 按压深度实时值
  const position = dataArray[9]; // 压缩位置
  const rate = dataArray[10]; // 压缩频率
  const maxDepth = dataArray[11]; // 最大压缩深度
  const isRebound = dataArray[12]; // 是否回弹 
  const ventCount = dataArray[13]; // 吹气次数
  const ventVolume = dataArray[14] * 10; // 吹气容量（需要×10恢复原值）
  const isVentilation = dataArray[15]; // 气道状态
  const ventDuration = dataArray[16] / 10; // 通气时长（需要÷10恢复原值，单位：秒）
  const maxVentVolume = dataArray[17] * 10; // 最大通气体积（需要×10恢复原值）

  console.log('数据头', header, '模型编号', model, '命令', command, '数据长度', length);

  // 更新 UI
  elements.compressionCount.textContent = count;
  elements.compressionDepth.textContent = depth + ' mm';
  
  // 更新按压状态图片显示
  updateCompressionStatus(depth);
  
  // 更新按压波形图数据（只在按压时更新）
  if (compressionChart) {
    // 只在按压深度>0时更新波形图
    if (depth > 0) {
      compressionChart.addData(depth);
      const compressionWaveStatus = document.getElementById('compressionWaveStatus');
      compressionWaveStatus.textContent = `实时: ${depth}mm`;
      compressionWaveStatus.style.color = depth >= 50 && depth <= 60 ? '#27ae60' : '#e74c3c';
    } else {
      // 无按压时保持上次状态，但不添加数据点
      const compressionWaveStatus = document.getElementById('compressionWaveStatus');
      if (compressionWaveStatus.textContent === '等待数据...') {
        compressionWaveStatus.textContent = '等待按压数据...';
        compressionWaveStatus.style.color = '#666';
      }
    }
  }
  
  const POSITION_MAP = {
    0: '没有按压',
    1: '偏上',
    2: '偏下',
    3: '偏左',
    4: '偏右',
    5: '正确'
  };
  const positionText = POSITION_MAP[position] || '未知位置';
  elements.compressionPosition.textContent = positionText; // 00没有按压 01偏上02偏下03偏左04偏右05正确
  elements.compressionRate.textContent = rate;
  
  // 最大按压深度：只有当新值大于当前最大值时才更新
  if (depth > maxDepthValue) {
    maxDepthValue = depth;
    elements.maxCompressionDepth.textContent = maxDepthValue + ' mm';
  }
  
  elements.isRecoil.textContent = isRebound === 1 ? '是' : '否';
  elements.isRecoil.className = 'status-boolean ' + (isRebound === 1 ? 'yes' : 'no');

  elements.isVentilation.textContent = isVentilation === 1 ? '打开' : '未打开';
  elements.ventilationCount.textContent = ventCount;
  elements.ventilationVolume.textContent = ventVolume + ' ml';
  elements.ventilationDuration.textContent = ventDuration.toFixed(1) + ' s'; // 显示1位小数
  
  // 最大通气量：只有当新值大于当前最大值时才更新
  if (ventVolume > maxVentValue) {
    maxVentValue = ventVolume;
    elements.maxVentilationVolume.textContent = maxVentValue + ' ml';
  }
  
  // 更新通气波形图数据（只在通气时更新）
  if (ventilationChart) {
    // 只在通气量>0或者气道状态为“打开”时更新波形图
    if (ventVolume > 0 || isVentilation === 1) {
      ventilationChart.addData(ventVolume);
      const ventilationWaveStatus = document.getElementById('ventilationWaveStatus');
      if (ventVolume > 0) {
        ventilationWaveStatus.textContent = `实时: ${ventVolume}ml`;
        ventilationWaveStatus.style.color = ventVolume >= 500 && ventVolume <= 600 ? '#27ae60' : '#e74c3c';
      } else {
        ventilationWaveStatus.textContent = '通气中...';
        ventilationWaveStatus.style.color = '#f39c12';
      }
    } else {
      // 无通气时保持上次状态，但不添加数据点
      const ventilationWaveStatus = document.getElementById('ventilationWaveStatus');
      if (ventilationWaveStatus.textContent === '等待数据...') {
        ventilationWaveStatus.textContent = '等待通气数据...';
        ventilationWaveStatus.style.color = '#666';
      }
    }
  }

  // 1. 按压计数和质量统计（只在深度>0 时算 1 次）
  if (depth > 0) {
    totalPress++;
    document.getElementById('totalPress').textContent = `${totalPress}/${TARGET_PRESS}`;
    
    // 按压质量统计
    if (depth > 60) {
      overDeep++;
      document.getElementById('overDeep').textContent = overDeep;
    } else if (depth < 50) {
      overShallow++;
      document.getElementById('overShallow').textContent = overShallow;
    } else {
      // 50-60mm 为正确范围
      correctPress++;
      document.getElementById('correctPress').textContent = correctPress;
    }
    
    // 按压位置统计（假设位置=5为正确）
    if (position === 5) {
      correctPosition++;
    }
    
    // 按压回弹统计（假设回弹=1为正确）
    if (isRebound === 1) {
      correctRecoil++;
    }
  }

  // 2. 吹气计数 & 质量统计（只在通气量>0 时算）
  if (ventVolume > 0) {
    totalVent++;
    document.getElementById('totalVent').textContent = `${totalVent}/${TARGET_VENT}`;

    // 吹气质量统计（标准：500-600ml）
    if (ventVolume > 600) {
      overVent++;
      document.getElementById('overVent').textContent = overVent;
    } else if (ventVolume < 500) {
      underVent++;
      document.getElementById('underVent').textContent = underVent;
    } else {
      // 500-600ml 为正确范围
      correctVent++;
      document.getElementById('correctVent').textContent = correctVent;
    }
  }

  // 3. 检查是否达到目标，自动发送结束指令
  if (totalPress >= TARGET_PRESS && totalVent >= TARGET_VENT) {
    console.log('训练完成，自动发送结束指令');
    sendEndCommand();
    showScoreModal(); // 显示评分弹窗
  }
}


// 发送开始训练命令
async function sendStartTrainingCommand() {
  try {
    bluetoothStatus.textContent = '正在发送开始训练命令...';
    bluetoothStatus.className = 'status-sending';

    await sendStartCommand();
    
    bluetoothStatus.textContent = '✅ 开始训练命令已发送';
    bluetoothStatus.className = 'status-connected';
    console.log('开始训练命令已成功发送');
    
    // 重置统计数据
    totalPress = totalVent = overDeep = overShallow = correctPress = correctPosition = correctRecoil = overVent = underVent = correctVent = 0;
    maxDepthValue = maxVentValue = 0; // 重置最大值记录
    document.getElementById('totalPress').textContent = '0/150';
    document.getElementById('totalVent').textContent = '0/10';
    document.getElementById('overDeep').textContent = '0';
    document.getElementById('overShallow').textContent = '0';
    document.getElementById('correctPress').textContent = '0';
    document.getElementById('overVent').textContent = '0';
    document.getElementById('underVent').textContent = '0';
    document.getElementById('correctVent').textContent = '0';
    // 重置最大值显示
    elements.maxCompressionDepth.textContent = '0 mm';
    elements.maxVentilationVolume.textContent = '0 ml';
    
    // 重置按压状态显示
    updateCompressionStatus(0);
    
    // 清空波形图
    if (compressionChart) {
      compressionChart.clear();
      document.getElementById('compressionWaveStatus').textContent = '等待数据...';
      document.getElementById('compressionWaveStatus').style.color = '#666';
    }
    if (ventilationChart) {
      ventilationChart.clear();
      document.getElementById('ventilationWaveStatus').textContent = '等待数据...';
      document.getElementById('ventilationWaveStatus').style.color = '#666';
    }
    
    // 重置评分
    currentScore = 0;
  } catch (error) {
    console.error('发送开始训练命令失败:', error);
    bluetoothStatus.textContent = '❌ 发送失败: ' + error.message;
    bluetoothStatus.className = 'status-disconnected';
  }
}

// 发送结束训练命令
async function sendEndTrainingCommand() {
  try {
    bluetoothStatus.textContent = '正在发送结束训练命令...';
    bluetoothStatus.className = 'status-sending';

    await sendEndCommand();
    
    bluetoothStatus.textContent = '✅ 结束训练命令已发送';
    bluetoothStatus.className = 'status-connected';
    console.log('结束训练命令已成功发送');
    
    // 显示评分弹窗（只有在有数据时才显示）
    if (totalPress > 0 || totalVent > 0) {
      showScoreModal();
    }
  } catch (error) {
    console.error('发送结束训练命令失败:', error);
    bluetoothStatus.textContent = '❌ 发送失败: ' + error.message;
    bluetoothStatus.className = 'status-disconnected';
  }
}







// 在全局作用域中添加日志数组
const consoleLogs = [];

// 保存原始console方法
const originalConsoleLog = console.log;
const originalConsoleError = console.error;
const originalConsoleWarn = console.warn;

// 重写console方法以捕获日志
console.log = function (...args) {
  originalConsoleLog.apply(console, args);
  consoleLogs.push({
    type: 'log',
    timestamp: new Date().toISOString(),
    message: args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg).join(' ')
  });
};

console.error = function (...args) {
  originalConsoleError.apply(console, args);
  consoleLogs.push({
    type: 'error',
    timestamp: new Date().toISOString(),
    message: args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg).join(' ')
  });
};

console.warn = function (...args) {
  originalConsoleWarn.apply(console, args);
  consoleLogs.push({
    type: 'warn',
    timestamp: new Date().toISOString(),
    message: args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg).join(' ')
  });
};

// 初始化函数
async function initApp() {
  // 初始化按压状态显示
  updateCompressionStatus(0);
  
  // 初始化波形图
  try {
    // 按压波形图：150个数据点，最大值80mm
    compressionChart = new WaveformChart('compressionWaveform', 80, '#e74c3c', '深度(mm)', 150);
    // 通气波形图：10个数据点，最大值800ml
    ventilationChart = new WaveformChart('ventilationWaveform', 800, '#3498db', '潮气量(ml)', 10);
    console.log('波形图初始化成功');
  } catch (error) {
    console.error('波形图初始化失败:', error);
  }
  
  // 原有初始化代码...

  // 新增打印日志按钮事件监听
  const printLogBtn = document.getElementById('printLog');
  if (printLogBtn) {
    printLogBtn.addEventListener('click', showLogs);
  }

  // 关闭日志按钮事件
  const closeLogBtn = document.getElementById('closeLog');
  if (closeLogBtn) {
    closeLogBtn.addEventListener('click', hideLogs);
  }

  // 复制日志按钮事件
  const copyLogBtn = document.getElementById('copyLog');
  if (copyLogBtn) {
    copyLogBtn.addEventListener('click', copyLogs);
  }

  // 点击 overlay 关闭日志
  const logOverlay = document.getElementById('logOverlay');
  if (logOverlay) {
    logOverlay.addEventListener('click', hideLogs);
  }
}

// 显示日志函数
function showLogs() {
  const logContent = document.getElementById('logContent');
  const logContainer = document.getElementById('logContainer');
  const logOverlay = document.getElementById('logOverlay');

  if (logContent && logContainer && logOverlay) {
    // 生成格式化的日志文本
    const logText = consoleLogs.map(entry => {
      const time = new Date(entry.timestamp).toLocaleTimeString();
      const type = entry.type.toUpperCase();
      return `[${time}] ${type}: ${entry.message}`;
    }).join('\n');

    logContent.textContent = logText || '暂无日志信息';
    logContainer.style.display = 'block';
    logOverlay.style.display = 'block';

    debugLog('显示日志对话框');
  }
}

// 隐藏日志函数
function hideLogs() {
  const logContainer = document.getElementById('logContainer');
  const logOverlay = document.getElementById('logOverlay');

  if (logContainer && logOverlay) {
    logContainer.style.display = 'none';
    logOverlay.style.display = 'none';
  }
}

// 复制日志函数
function copyLogs() {
  const logContent = document.getElementById('logContent');

  if (logContent) {
    const tempTextArea = document.createElement('textarea');
    tempTextArea.value = logContent.textContent;
    document.body.appendChild(tempTextArea);
    tempTextArea.select();

    try {
      const successful = document.execCommand('copy');
      const msg = successful ? '成功' : '失败';
      alert(`日志复制${msg}`);
      debugLog(`日志复制${msg}`);
    } catch (err) {
      alert('复制失败: ' + err);
      debugLog('日志复制失败: ' + err);
    }

    document.body.removeChild(tempTextArea);
  }
}

function debugLog(message) {
  const debugInfo = document.getElementById('debugInfo');
  if (debugInfo) {
    const logEntry = document.createElement('div');
    logEntry.textContent = `${new Date().toLocaleTimeString()}: ${message}`;
    debugInfo.appendChild(logEntry);
    debugInfo.scrollTop = debugInfo.scrollHeight;
  }
  console.log(message);
}

// 绑定点击事件
connectBtn.addEventListener('click', connectToDevice);
clearBtn.addEventListener('click', sendStartTrainingCommand);  // 改为开始训练
stopBtn.addEventListener('click', sendEndTrainingCommand);     // 改为结束训练
// printLog.addEventListener('click', initApp);
copyLog.addEventListener('click', showLogs);
document.addEventListener('DOMContentLoaded', initApp);

// 添加窗口大小变化监听器
window.addEventListener('resize', function() {
  if (compressionChart) {
    compressionChart.resize();
  }
  if (ventilationChart) {
    ventilationChart.resize();
  }
});

// 评分弹窗事件监听
document.getElementById('closeModal').addEventListener('click', closeScoreModal);
document.getElementById('restartTraining').addEventListener('click', restartTraining);

// 点击弹窗背景关闭
document.getElementById('scoreModal').addEventListener('click', function(e) {
  if (e.target === this) {
    closeScoreModal();
  }
});